(function ($) {
  $._spritely = {
    // shared methods and variables used by spritely plugin
    animate: function (options) {
      var el = $(options.el);
      var el_id = el.attr('id');
      if (!$._spritely.instances[el_id]) {
        return this;
      }
      options = $.extend(options, $._spritely.instances[el_id] || {});
      if (options.play_frames && !$._spritely.instances[el_id]['remaining_frames']) {
        $._spritely.instances[el_id]['remaining_frames'] = options.play_frames + 1;
      }
      if (options.type == 'sprite' && options.fps) {
        var frames;
        var animate = function (el) {
          var w = options.width, h = options.height;
          if (!frames) {
            frames = [];
            total = 0
            for (var i = 0; i < options.no_of_frames; i++) {
              frames[frames.length] = (0 - total);
              total += w;
            }
          }

          if (options.rewind == true) {
            if ($._spritely.instances[el_id]['current_frame'] <= 0) {
              $._spritely.instances[el_id]['current_frame'] = frames.length - 1;
            } else {
              $._spritely.instances[el_id]['current_frame'] = $._spritely.instances[el_id]['current_frame'] - 1;
            }
            ;
          } else {
            if ($._spritely.instances[el_id]['current_frame'] >= frames.length - 1) {
              $._spritely.instances[el_id]['current_frame'] = 0;
            } else {
              $._spritely.instances[el_id]['current_frame'] = $._spritely.instances[el_id]['current_frame'] + 1;
            }
          }

          var yPos = $._spritely.getBgY(el);
          el.css('background-position', frames[$._spritely.instances[el_id]['current_frame']] + 'px ' + yPos);
          if (options.bounce && options.bounce[0] > 0 && options.bounce[1] > 0) {
            var ud = options.bounce[0]; // up-down
            var lr = options.bounce[1]; // left-right
            var ms = options.bounce[2]; // milliseconds
            el
              .animate({top: '+=' + ud + 'px', left: '-=' + lr + 'px'}, ms)
              .animate({top: '-=' + ud + 'px', left: '+=' + lr + 'px'}, ms);
          }
        }
        if ($._spritely.instances[el_id]['remaining_frames'] && $._spritely.instances[el_id]['remaining_frames'] > 0) {
          $._spritely.instances[el_id]['remaining_frames']--;
          if ($._spritely.instances[el_id]['remaining_frames'] == 0) {
            $._spritely.instances[el_id]['remaining_frames'] = -1;
            delete $._spritely.instances[el_id]['remaining_frames'];
            return;
          } else {
            animate(el);
          }
        } else if ($._spritely.instances[el_id]['remaining_frames'] != -1) {
          animate(el);
        }
      } else if (options.type == 'pan') {
        if (!$._spritely.instances[el_id]['_stopped']) {
          if (options.dir == 'up') {
            $._spritely.instances[el_id]['l'] = $._spritely.getBgX(el).replace('px', '');
            $._spritely.instances[el_id]['t'] = ($._spritely.instances[el_id]['t'] - (options.speed || 1)) || 0;
          }
          else if (options.dir == 'down') {
            $._spritely.instances[el_id]['l'] = $._spritely.getBgX(el).replace('px', '');
            $._spritely.instances[el_id]['t'] = ($._spritely.instances[el_id]['t'] + (options.speed || 1)) || 0;
          }
          else if (options.dir == 'left') {
            $._spritely.instances[el_id]['l'] = ($._spritely.instances[el_id]['l'] - (options.speed || 1)) || 0;
            $._spritely.instances[el_id]['t'] = $._spritely.getBgY(el).replace('px', '');
          } else {
            $._spritely.instances[el_id]['l'] = ($._spritely.instances[el_id]['l'] + (options.speed || 1)) || 0;
            $._spritely.instances[el_id]['t'] = $._spritely.getBgY(el).replace('px', '');
          }

          // When assembling the background-position string, care must be taken
          // to ensure correct formatting.. <ricky.hewitt@artlogic.net>
          var bg_left = $._spritely.instances[el_id]['l'].toString();
          if (bg_left.indexOf('%') == -1) {
            bg_left += 'px ';
          } else {
            bg_left += ' ';
          }

          var bg_top = $._spritely.instances[el_id]['t'].toString();
          if (bg_top.indexOf('%') == -1) {
            bg_top += 'px ';
          } else {
            bg_top += ' ';
          }

          $(el).css('background-position', bg_left + bg_top);
        }
      }
      $._spritely.instances[el_id]['options'] = options;
      window.setTimeout(function () {
        $._spritely.animate(options);
      }, parseInt(1000 / options.fps));
    },
    randomIntBetween: function (lower, higher) {
      return parseInt(rand_no = Math.floor((higher - (lower - 1)) * Math.random()) + lower);
    },
    getBgY: function (el) {
      if ($.browser) {
        // fixme - the background-position property does not work
        // correctly in IE so we have to hack it here... Not ideal
        // especially as $.browser is depricated
        var bgY = $(el).css('background-position-y') || '0';
      } else {
        var bgY = ($(el).css('background-position') || ' ').split(' ')[1];
      }
      return bgY;
    },
    getBgX: function (el) {
      if ($.browser) {
        // see note, above
        var bgX = $(el).css('background-position-x') || '0';
      } else {
        var bgX = ($(el).css('background-position') || ' ').split(' ')[0];
      }
      return bgX;
    },
    get_rel_pos: function (pos, w) {
      // return the position of an item relative to a background
      // image of width given by w
      var r = pos;
      if (pos < 0) {
        while (r < 0) {
          r += w;
        }
      } else {
        while (r > w) {
          r -= w;
        }
      }
      return r;
    }
  };
  $.fn.extend({
    spritely: function (options) {
      var options = $.extend({
        type: 'sprite',
        do_once: false,
        width: null,
        height: null,
        fps: 12,
        no_of_frames: 2,
        stop_after: null
      }, options || {});
      var el_id = $(this).attr('id');
      if (!$._spritely.instances) {
        $._spritely.instances = {};
      }
      if (!$._spritely.instances[el_id]) {
        $._spritely.instances[el_id] = {current_frame: -1};
      }
      $._spritely.instances[el_id]['type'] = options.type;
      $._spritely.instances[el_id]['depth'] = options.depth;
      options.el = this;
      options.width = options.width || $(this).width() || 100;
      options.height = options.height || $(this).height() || 100;
      var get_rate = function () {
        return parseInt(1000 / options.fps);
      };
      if (!options.do_once) {
        window.setTimeout(function () {
          $._spritely.animate(options);
        }, get_rate(options.fps));
      } else {
        $._spritely.animate(options);
      }
      return this; // so we can chain events
    },
    sprite: function (options) {
      var options = $.extend({
        type: 'sprite',
        bounce: [0, 0, 1000] // up-down, left-right, milliseconds
      }, options || {});
      return $(this).spritely(options);
    },
    pan: function (options) {
      var options = $.extend({
        type: 'pan',
        dir: 'left',
        continuous: true,
        speed: 1 // 1 pixel per frame
      }, options || {});
      return $(this).spritely(options);
    },
    flyToTap: function (options) {
      var options = $.extend({
        el_to_move: null,
        type: 'moveToTap',
        ms: 1000, // milliseconds
        do_once: true
      }, options || {});
      if (options.el_to_move) {
        $(options.el_to_move).active();
      }
      if ($._spritely.activeSprite) {
        if (window.Touch) { // iphone method see http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone/9 or http://www.nimblekit.com/tutorials.html for clues...
          $(this)[0].ontouchstart = function (e) {
            var el_to_move = $._spritely.activeSprite;
            var touch = e.touches[0];
            var t = touch.pageY - (el_to_move.height() / 2);
            var l = touch.pageX - (el_to_move.width() / 2);
            el_to_move.animate({
              top: t + 'px',
              left: l + 'px'
            }, 1000);
          };
        } else {
          $(this).click(function (e) {
            var el_to_move = $._spritely.activeSprite;
            $(el_to_move).stop(true);
            var w = el_to_move.width();
            var h = el_to_move.height();
            var l = e.pageX - (w / 2);
            var t = e.pageY - (h / 2);
            el_to_move.animate({
              top: t + 'px',
              left: l + 'px'
            }, 1000);
          });
        }
      }
      return this;
    },
    // isDraggable requires jQuery ui
    isDraggable: function (options) {
      if ((!$(this).draggable)) {
        //console.log('To use the isDraggable method you need to load jquery-ui.js');
        return this;
      }
      var options = $.extend({
        type: 'isDraggable',
        start: null,
        stop: null,
        drag: null
      }, options || {});
      var el_id = $(this).attr('id');
      if (!$._spritely.instances[el_id]) {
        return this;
      }
      $._spritely.instances[el_id].isDraggableOptions = options;
      $(this).draggable({
        start: function () {
          var el_id = $(this).attr('id');
          $._spritely.instances[el_id].stop_random = true;
          $(this).stop(true);
          if ($._spritely.instances[el_id].isDraggableOptions.start) {
            $._spritely.instances[el_id].isDraggableOptions.start(this);
          }
        },
        drag: options.drag,
        stop: function () {
          var el_id = $(this).attr('id');
          $._spritely.instances[el_id].stop_random = false;
          if ($._spritely.instances[el_id].isDraggableOptions.stop) {
            $._spritely.instances[el_id].isDraggableOptions.stop(this);
          }
        }
      });
      return this;
    },
    active: function () {
      // the active sprite
      $._spritely.activeSprite = this;
      return this;
    },
    activeOnClick: function () {
      // make this the active script if clicked...
      var el = $(this);
      if (window.Touch) { // iphone method see http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone/9 or http://www.nimblekit.com/tutorials.html for clues...
        el[0].ontouchstart = function (e) {
          $._spritely.activeSprite = el;
        };
      } else {
        el.click(function (e) {
          $._spritely.activeSprite = el;
        });
      }
      return this;
    },
    spRandom: function (options) {
      var options = $.extend({
        top: 50,
        left: 50,
        right: 290,
        bottom: 320,
        speed: 4000,
        pause: 0
      }, options || {});
      var el_id = $(this).attr('id');
      if (!$._spritely.instances[el_id]) {
        return this;
      }
      if (!$._spritely.instances[el_id].stop_random) {
        var r = $._spritely.randomIntBetween;
        var t = r(options.top, options.bottom);
        var l = r(options.left, options.right);
        $('#' + el_id).animate({
          top: t + 'px',
          left: l + 'px'
        }, options.speed)
      }
      window.setTimeout(function () {
        $('#' + el_id).spRandom(options);
      }, options.speed + options.pause)
      return this;
    },
    makeAbsolute: function () {
      // remove an element from its current position in the DOM and
      // position it absolutely, appended to the body tag.
      return this.each(function () {
        var el = $(this);
        var pos = el.position();
        el.css({position: "absolute", marginLeft: 0, marginTop: 0, top: pos.top, left: pos.left})
          .remove()
          .appendTo("body");
      });

    },
    spSet: function (prop_name, prop_value) {
      var el_id = $(this).attr('id');
      $._spritely.instances[el_id][prop_name] = prop_value;
      return this;
    },
    spGet: function (prop_name, prop_value) {
      var el_id = $(this).attr('id');
      return $._spritely.instances[el_id][prop_name];
    },
    spStop: function (bool) {
      $(this).each(function () {
        var el_id = $(this).attr('id');
        $._spritely.instances[el_id]['_last_fps'] = $(this).spGet('fps');
        $._spritely.instances[el_id]['_stopped'] = true;
        $._spritely.instances[el_id]['_stopped_f1'] = bool;
        if ($._spritely.instances[el_id]['type'] == 'sprite') {
          $(this).spSet('fps', 0);
        }
        if (bool) {
          // set background image position to 0
          var bp_top = $._spritely.getBgY($(this));
          $(this).css('background-position', '0 ' + bp_top);
        }
      });
      return this;
    },
    spStart: function () {
      $(this).each(function () {
        var el_id = $(this).attr('id');
        var fps = $._spritely.instances[el_id]['_last_fps'] || 12;
        $._spritely.instances[el_id]['_stopped'] = false;
        if ($._spritely.instances[el_id]['type'] == 'sprite') {
          $(this).spSet('fps', fps);
        }
      });
      return this;
    },
    destroy: function () {
      var el = $(this);
      var el_id = $(this).attr('id');
      delete $._spritely.instances[el_id]
      return this;
    }
  })
})(jQuery);
